/*
** Haaf's Game Engine 1.7
** Copyright (C) 2003-2007, Relish Games
** hge.relishgames.com
**
** hgeFont helper class implementation
*/


#include "..\..\include\hgefont.h"
#include <math.h>

const char FNTHEADERTAG[] = "[HGEFONT]";
const char FNTBITMAPTAG[] = "Bitmap";
const char FNTCHARTAG[]   = "Char";


HGE *hgeFont::hge=0;
char hgeFont::buffer[1024];

// Font objects usage count
static int ft_usage_count = 0;
// A handle to the FreeType library
static FT_Library ft_lib;

#undef __FTERRORS_H__
#define FT_ERRORDEF( e, v, s ) s,
#define FT_ERROR_START_LIST static const char* ft_errors[] = {
#define FT_ERROR_END_LIST 0};
#include FT_ERRORS_H

hgeFont::hgeFont(const char *fileName, bool bMipmap)
{
	hge = hgeCreate(HGE_VERSION);

	// Setup variables	
	strcpy_s(m_facename, hge->Resource_MakePath(fileName));
	m_antiAliased = true;
	m_glyphPageLoaded = NULL;

	fHeight = 16.0f;
	fScale = 1.0f;
	fProportion = 1.0f;
	fRot = 0.0f;
	fTracking = 0.0f;
	fSpacing = 1.0f;
	
	fZ = 0.5f;
	nBlend = BLEND_COLORMUL | BLEND_ALPHABLEND | BLEND_NOZWRITE;
	dwCol = 0xFFFFFFFF;

	// Load font description
	FT_Error error;
	if (!ft_usage_count++)
		error = FT_Init_FreeType(&ft_lib);
	if (error)
	{
		throw std::runtime_error("hgeFont:: Failed to init FreeType lib, error was: " +
			(error < FT_Err_Max) ? ft_errors[error] : "unknown error");
	}

	UpdateFont();
}

hgeFont::~hgeFont()
{
	Free();
	hge->Release();
}

void hgeFont::Free()
{
	if (!m_face)
		return;

	m_cp_map.clear();
	FT_Done_Face(m_face);
	m_face = 0;
}

void hgeFont::Render(float x, float y, int align, const char *string)
{
	float glyph_pos_x = x;
	float glyph_pos_y = y;
	
	float base_y = y + GetBaseline(fScale);
	
	wchar_t *pwText;
	pwText = new wchar_t[1024];
	MultiByteToWideChar(CP_ACP, 0, (LPCSTR)string, 1024, (LPWSTR)pwText, 1024);
	wchar_t *startText = pwText;

	//align &= HGETEXT_HORZMASK;
	//if(align == HGETEXT_RIGHT)
	//	glyph_pos_x -= GetStringWidth(string, false);
	//if(align == HGETEXT_CENTER)
	//	glyph_pos_x -= int(GetStringWidth(string, false)/2.0f);

	while(*pwText)
	{
		if(*pwText=='\n')
		{
			base_y += fScale*m_height;
			glyph_pos_x = x;
			/*if(align == HGETEXT_RIGHT)
				glyph_pos_x -= GetStringWidth(string+1, false);
			if(align == HGETEXT_CENTER)
				glyph_pos_x -= int(GetStringWidth(string+1, false)/2.0f);*/
		}
		else
		{
			hgeFontGlyph* glyph;
			if (*pwText == 'q')
				int i = 0;
			if ( (glyph = GetGlyphData( *pwText )) ) // NB: assignment
			{
				hgeSprite* img = glyph->GetImage();
				glyph_pos_y = base_y + img->GetOffsetY();
				/*img->SetColor(dwCol);
				img->SetZ(fZ);
				img->SetBlendMode(nBlend);*/
				img->RenderEx(glyph_pos_x, glyph_pos_y, fRot, fScale*fProportion, fScale);
				glyph_pos_x += (glyph->GetAdvance(fScale) - img->GetOffsetX());
			}			
		}
		pwText++;
	}
	delete[] startText;
}

void hgeFont::printf(float x, float y, int align, const char *format, ...)
{
	char	*pArg=(char *) &format+sizeof(format);

	vsnprintf_s(buffer, sizeof(buffer)-1, _TRUNCATE, format, pArg);
	buffer[sizeof(buffer)-1]=0;
	//vsprintf(buffer, format, pArg);
	
	Render(x, y, align, buffer);
}

void hgeFont::printfb(float x, float y, float w, float h, int align, const char *format, ...)
{
	char	chr, *pbuf, *prevword, *linestart;
	int		i,lines=0;
	float	tx, ty, hh, ww;
	char	*pArg=(char *) &format+sizeof(format);

	vsnprintf_s(buffer, sizeof(buffer)-1, _TRUNCATE, format, pArg);
	buffer[sizeof(buffer)-1]=0;
	//vsprintf(buffer, format, pArg);

	linestart=buffer;
	pbuf=buffer;
	prevword=0;

	for(;;)
	{
		i=0;
		while(pbuf[i] && pbuf[i]!=' ' && pbuf[i]!='\n') i++;

		chr=pbuf[i];
		pbuf[i]=0;
		//ww=GetStringWidth(linestart);
		ww = 0;
		pbuf[i]=chr;

		if(ww > w)
		{
			if(pbuf==linestart)
			{
				pbuf[i]='\n';
				linestart=&pbuf[i+1];
			}
			else
			{
				*prevword='\n';
				linestart=prevword+1;
			}

			lines++;
		}

		if(pbuf[i]=='\n')
		{
			prevword=&pbuf[i];
			linestart=&pbuf[i+1];
			pbuf=&pbuf[i+1];
			lines++;
			continue;
		}

		if(!pbuf[i]) {lines++;break;}

		prevword=&pbuf[i];
		pbuf=&pbuf[i+1];
	}
	
	tx=x;
	ty=y;
	hh=fHeight*fSpacing*fScale*lines;

	switch(align & HGETEXT_HORZMASK)
	{
		case HGETEXT_LEFT: break;
		case HGETEXT_RIGHT: tx+=w; break;
		case HGETEXT_CENTER: tx+=int(w/2); break;
	}

	switch(align & HGETEXT_VERTMASK)
	{
		case HGETEXT_TOP: break;
		case HGETEXT_BOTTOM: ty+=h-hh; break;
		case HGETEXT_MIDDLE: ty+=int((h-hh)/2); break;
	}
	
	Render(tx, ty, align, buffer);
}

void hgeFont::SetColor(DWORD col)
{
	dwCol = col;
}

void hgeFont::SetZ(float z)
{
	fZ = z;
}

void hgeFont::SetBlendMode(int blend)
{
	nBlend = blend;
}

char *hgeFont::_get_line(char *file, char *line)
{
	int i=0;

	if(!file[i]) return 0;

	while(file[i] && file[i]!='\n' && file[i]!='\r')
	{
		line[i]=file[i];
		i++;
	}
	line[i]=0;

	while(file[i] && (file[i]=='\n' || file[i]=='\r')) i++;

	return file + i;
}

bool hgeFont::UpdateFont()
{
	FT_Error error;
	error = FT_New_Face( ft_lib, m_facename, 0, &m_face);
	if (error)
	{
		throw std::runtime_error("hgeFont::updateFont: Failed to create face from font file, error was: " +
			(error < FT_Err_Max) ? ft_errors[error] : "unknown error");
	}

	// check that default Unicode character map is available
	if (!m_face->charmap)
	{
		FT_Done_Face(m_face);
		m_face = 0;
		throw std::runtime_error("hgeFont::updateFont: The font does not have a Unicode charmap, and cannot be used.");
	}

	float hps = fHeight * 64 * fScale;
	float vps = fHeight * 64 * fScale;
	if (FT_Set_Char_Size(m_face, FT_F26Dot6(hps), FT_F26Dot6(vps), 96, 96))
	{
		throw std::runtime_error("FreeTypeFont::updateFont: Failed to set face size, error was: " +
			(error < FT_Err_Max) ? ft_errors[error] : "unknown error");
	}

	if (m_face->face_flags & FT_FACE_FLAG_SCALABLE)
	{
		//float x_scale = d_fontFace->size->metrics.x_scale * FT_POS_COEF * (1.0/65536.0);
		float y_scale = m_face->size->metrics.y_scale * float(FT_POS_COEF) * (1.0f / 65536.0f);
		m_ascender = m_face->ascender * y_scale;
		m_descender = m_face->descender * y_scale;
		m_height = m_face->height * y_scale;
	}
	else
	{
		m_ascender = m_face->size->metrics.ascender * float(FT_POS_COEF);
		m_descender = m_face->size->metrics.descender * float(FT_POS_COEF);
		m_height = m_face->size->metrics.height * float(FT_POS_COEF);
	}

	// Create an empty FontGlyph structure for every glyph of the font
	FT_UInt gindex;
	FT_ULong codepoint = FT_Get_First_Char(m_face, &gindex);
	FT_ULong max_codepoint = codepoint;
	while (gindex)
	{
		if (max_codepoint < codepoint)
			max_codepoint = codepoint;

		// load-up required glyph metrics (don't render)
		if (FT_Load_Char(m_face, codepoint, FT_LOAD_DEFAULT | FT_LOAD_FORCE_AUTOHINT))
			continue; // glyph error

		float adv = m_face->glyph->metrics.horiAdvance * float(FT_POS_COEF);

		// create a new empty FontGlyph with given character code
		m_cp_map[codepoint] = hgeFontGlyph(adv);

		// proceed to next glyph
		codepoint = FT_Get_Next_Char(m_face, codepoint, &gindex);
	}

	SetMaxCodepoint(max_codepoint);
}

void hgeFont::SetMaxCodepoint(UINT32 codepoint)
{
	m_maxCodepoint = codepoint;

	if (m_glyphPageLoaded != NULL)
		delete[] m_glyphPageLoaded;

	UINT npages = (codepoint + GLYPHS_PER_PAGE) / GLYPHS_PER_PAGE;
	UINT size = (npages + BITS_PER_UINT - 1) / BITS_PER_UINT;
	m_glyphPageLoaded = new UINT[size];
	memset(m_glyphPageLoaded, 0, size * sizeof(UINT));
}

hgeFontGlyph* hgeFont::GetGlyphData(UINT32 codepoint)
{
	if (codepoint > m_maxCodepoint)
		return 0;

	if (m_glyphPageLoaded)
	{
		// Check if glyph page has been rasterised
		UINT page = codepoint / GLYPHS_PER_PAGE;
		UINT mask = 1 << (page & (BITS_PER_UINT - 1));
		if ( !(m_glyphPageLoaded[page / BITS_PER_UINT] & mask) )
		{
			m_glyphPageLoaded[page / BITS_PER_UINT] |= mask;
			Rasterise(codepoint & ~(GLYPHS_PER_PAGE - 1), codepoint | (GLYPHS_PER_PAGE - 1));
		}
	}

	CodepointMap::iterator pos = m_cp_map.find(codepoint);
	return (pos != m_cp_map.end()) ? &pos->second : 0;
}

UINT hgeFont::GetTextureSize(CodepointMap::iterator s, CodepointMap::iterator e) const
{
	UINT texsize = 32;		// start with 32x32
	UINT max_texsize = hge->Gfx_GetMaxTextureSize();
	UINT glyph_count = 0;

	// Compute approximatively the optimal texture size for font
	while (texsize < max_texsize)
	{
		UINT x = INTER_GLYPH_PAD_SPACE, y = INTER_GLYPH_PAD_SPACE;
		UINT yb = INTER_GLYPH_PAD_SPACE;
		for (CodepointMap::iterator c = s; c != e; ++c)
		{
			// skip glyphs that are already rendered
			if (c->second.GetImage())
				continue;

			// load glyph metrics (don't render)
			if (FT_Load_Char(m_face, c->first, FT_LOAD_DEFAULT | FT_LOAD_FORCE_AUTOHINT))
				continue;

			UINT glyph_w = int (ceil(m_face->glyph->metrics.width * FT_POS_COEF)) +
				INTER_GLYPH_PAD_SPACE;
			UINT glyph_h = int (ceil(m_face->glyph->metrics.height * FT_POS_COEF)) +
				INTER_GLYPH_PAD_SPACE;

			x += glyph_w;
			if (x > texsize)
			{
				x = INTER_GLYPH_PAD_SPACE;
				y = yb;
			}
			UINT yy = y + glyph_h;
			if (yy > texsize)
				goto too_small;

			if (yy > yb)
				yb = yy;

			++glyph_count;
		}
		// Okay, the texture size is enough for holding our glyphs
		break;

too_small:
		texsize *= 2;
	}

	return glyph_count ? texsize : 0;
}


void hgeFont::Rasterise(UINT32 start_codepoint, UINT32 end_codepoint)
{
	CodepointMap::iterator s = m_cp_map.lower_bound(start_codepoint);
	if (s == m_cp_map.end())
		return;

	CodepointMap::iterator orig_s = s;
	CodepointMap::iterator e = m_cp_map.upper_bound(end_codepoint);
	while (true)
	{
		// Create a new Imageset for glyphs
		UINT texsize = GetTextureSize(s, e);

		// If all glyphs were already rendered, do nothing
		if (!texsize)
			break;
		
		// create a empty texture
		HTEXTURE hTexture = hge->Texture_Create(texsize, texsize);
		m_glyphImages.push_back(hTexture);

		// Create a memory buffer where we will render our glyphs
		UINT32 *mem_buffer = new UINT32 [texsize * texsize];
		memset(mem_buffer, 0, texsize * texsize * sizeof(UINT32));

		// Go ahead, line by line, top-left to bottom-right
		UINT x = INTER_GLYPH_PAD_SPACE, y = INTER_GLYPH_PAD_SPACE;
		UINT yb = INTER_GLYPH_PAD_SPACE;

		// Set to true when we finish rendering all glyphs we were asked to
		bool finished = false;
		// Set to false when we reach m_cp_map.end() and we start going backward
		bool forward = true;

		/* To conserve texture space we will render more glyphs than asked,
		* but never less than asked. First we render all glyphs from s to e
		* and after that we render glyphs until we reach m_cp_map.end(),
		* and if there's still free texture space we will go backward
		* from s until we hit m_cp_map.begin().
		*/
		while (s != m_cp_map.end())
		{
			// Check if we finished rendering all the required glyphs
			finished |= (s == e);

			// Check if glyph already rendered
			if (!s->second.GetImage())
			{
				// Render the glyph
				if (FT_Load_Char(m_face, s->first, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT |
					(m_antiAliased ? FT_LOAD_TARGET_NORMAL : FT_LOAD_TARGET_MONO)))
				{
					/*std::stringstream err;
					err << "Font::loadFreetypeGlyph - Failed to load glyph for codepoint: ";
					err << static_cast<unsigned int>(s->first);
					err << ".  Will use an empty image for this glyph!";
					hge->System_Log(err.str());*/

					// Create a 'null' image for this glyph so we do not seg later
					hgeSprite* spr = new hgeSprite(hTexture, 0, 0, 0, 0);
					((hgeFontGlyph &)s->second).SetImage(spr);
				}
				else
				{
					UINT glyph_w = m_face->glyph->bitmap.width + INTER_GLYPH_PAD_SPACE;
					UINT glyph_h = m_face->glyph->bitmap.rows + INTER_GLYPH_PAD_SPACE;

					// Check if glyph right margin does not exceed texture size
					UINT x_next = x + glyph_w;
					if (x_next > texsize)
					{
						x = INTER_GLYPH_PAD_SPACE;
						x_next = x + glyph_w;
						y = yb;
					}

					// Check if glyph bottom margine does not exceed texture size
					UINT y_bot = y + glyph_h;
					if (y_bot > texsize)
						break;

					// Copy rendered glyph to memory buffer in RGBA format
					DrawGlyphToBuffer(mem_buffer + (y * texsize) + x, texsize);

					// Create a new sprite for the glyph					
					float offsetX = m_face->glyph->metrics.horiBearingX * static_cast<float>(FT_POS_COEF);
					float offsetY = -m_face->glyph->metrics.horiBearingY * static_cast<float>(FT_POS_COEF);
				
					hgeSprite* spr = new hgeSprite(hTexture, x, y, glyph_w - INTER_GLYPH_PAD_SPACE, glyph_h - INTER_GLYPH_PAD_SPACE);
					spr->SetOffset(offsetX, offsetY);
					((hgeFontGlyph &)s->second).SetImage(spr);

					// Advance to next position
					x = x_next;
					if (y_bot > yb)
					{
						yb = y_bot;
					}
				}
			}

			// Go to next glyph, if we are going forward
			if (forward)
			{
				if (++s == m_cp_map.end())
				{
					finished = true;
					forward = false;
					s = orig_s;
				}
				// Go to previous glyph, if we are going backward
				if (!forward)
					if ((s == m_cp_map.begin()) || (--s == m_cp_map.begin()))
						break;
			}
		}

		// Copy our memory buffer into the texture and free it
		hge->Texture_LoadFromMemory(hTexture, mem_buffer, texsize, texsize, HGE_RGBA);
		delete [] mem_buffer;

		if (finished)
			break;
	}
}

void hgeFont::DrawGlyphToBuffer(UINT32 *buffer, UINT buf_width) const
{
	FT_Bitmap *glyph_bitmap = &m_face->glyph->bitmap;

	for (int i = 0; i < glyph_bitmap->rows; ++i)
	{
		UCHAR *src = glyph_bitmap->buffer + (i * glyph_bitmap->pitch);
		switch (glyph_bitmap->pixel_mode)
		{
		case FT_PIXEL_MODE_GRAY:
			{
				UCHAR *dst = reinterpret_cast<UCHAR*>(buffer);
				for (int j = 0; j < glyph_bitmap->width; ++j)
				{
					// RGBA
					*dst++ = 0xFF;
					*dst++ = 0xFF;
					*dst++ = 0xFF;
					*dst++ = *src++;
				}
			}
			break;

		case FT_PIXEL_MODE_MONO:
			for (int j = 0; j < glyph_bitmap->width; ++j)
				buffer [j] = (src [j / 8] & (0x80 >> (j & 7))) ? 0xFFFFFFFF : 0x00000000;
			break;

		default:
			throw std::runtime_error("Font::drawGlyphToBuffer: "
				"The glyph could not be drawn because the pixel mode is "
				"unsupported.");
			break;
		}

		buffer += buf_width;
	}
}